/**
 * Copyright (c) 2010
 * Bert Young. UESTC. 
 */
#include "debug.h"

#include <errno.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <cassert>
#include "ServerSocket.h"

ServerSocket::ServerSocket() : sockfd(-1)
{
	this->epfd = epoll_create(1);
#ifndef CHINESE
	debug("%s = creat MAIN epollfd %d",__PRETTY_FUNCTION__, epfd);
#else
	debug("创建主监听epoll描述符%d", epfd);
#endif
}

ServerSocket::~ServerSocket()
{
#ifndef CHINESE
	debug("%s = close MAIN epollfd %d",__PRETTY_FUNCTION__, epfd);
	debug("%s = close LISTEN socket %d",__PRETTY_FUNCTION__, sockfd);
#else
	debug("关闭主监听epoll描述符%d", epfd);
	debug("关闭主监听socket描述符%d", sockfd);
#endif
	TEMP_FAILURE_RETRY( ::close(epfd) );
	if ( -1 != sockfd )
	{
		::shutdown(sockfd, SHUT_RDWR);
		TEMP_FAILURE_RETRY( ::close(sockfd) );
	}
}

bool ServerSocket::bind( const in_port_t port )
{
	assert( port > 0 && "server port error!!!" );
	if ( this->sockfd > 0 )
		return false;
		
	this->sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if ( this->sockfd < 0 )
		return false;
#ifndef CHINESE
	debug("%s = creat LISTEN socket %d",__PRETTY_FUNCTION__, sockfd);
#else
	debug("创建监听服务socket %d", sockfd);
#endif

	// These socket options will be inherited by the socket fd returned from accept.
	int reuse = 1;
	if ( -1 == ::setsockopt(this->sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) )
	{
		TEMP_FAILURE_RETRY(::close(this->sockfd));
		return false;
	}
	socklen_t windowsize = 128 * 1024;
	if ( -1 == ::setsockopt(this->sockfd, SOL_SOCKET, SO_RCVBUF, &windowsize, sizeof(windowsize) ))
	{
		TEMP_FAILURE_RETRY(::close(this->sockfd));
		return false;
	}
	if ( -1 == ::setsockopt(this->sockfd, SOL_SOCKET, SO_SNDBUF, &windowsize, sizeof(windowsize) ))
	{
		TEMP_FAILURE_RETRY(::close(this->sockfd));
		return false;
	}

	struct sockaddr_in serv;
	bzero(&serv, sizeof(serv));
	serv.sin_family = AF_INET;
	serv.sin_addr.s_addr = htonl(INADDR_ANY);
	serv.sin_port = htons(port);

	int ret = ::bind(this->sockfd, (struct sockaddr*)&serv, sizeof(serv));
	if ( ret < 0 )
	{
		TEMP_FAILURE_RETRY(::close(this->sockfd));
		this->sockfd = -1;
		return false;
	}
	ret = ::listen(this->sockfd, ServerSocket::LISTENQ);
	if ( ret < 0 )
	{
		TEMP_FAILURE_RETRY(::close(this->sockfd));
		return false;
	}

	struct epoll_event ev;
	bzero(&ev, sizeof(ev));
	ev.events = EPOLLIN;
	ret = epoll_ctl(this->epfd, EPOLL_CTL_ADD, this->sockfd, &ev);
	if ( 0 != ret )
	{
		TEMP_FAILURE_RETRY(::close(this->epfd));
		TEMP_FAILURE_RETRY(::close(this->sockfd));
		return false;
	}

	return true;
}

int ServerSocket::accept(struct sockaddr_in * client)
{
	socklen_t len = sizeof(struct sockaddr_in);
	bzero(client, len);

	struct epoll_event ev;
	int ret = epoll_wait(this->epfd, &ev, 1, ServerSocket::ACCEPT_TIMEOUT);
	if ( ret == 1 && (ev.events & EPOLLIN) )
		return TEMP_FAILURE_RETRY(::accept(this->sockfd, (struct sockaddr *)client, &len));
	return -1;
}

